package cn.labbook.bluetooth.service.impl;

import cn.labbook.bluetooth.common.SmsTemplateEnum;
import cn.labbook.bluetooth.common.SysExceptionEnum;
import cn.labbook.bluetooth.entity.Ammeter;
import cn.labbook.bluetooth.entity.ApplyPay;
import cn.labbook.bluetooth.entity.ApplyRecord;
import cn.labbook.bluetooth.entity.wxpay.WXPayment;
import cn.labbook.bluetooth.exception.SysException;
import cn.labbook.bluetooth.mapper.ApplyPayMapper;
import cn.labbook.bluetooth.pojo.dto.PrepaymentResultDTO;
import cn.labbook.bluetooth.service.IAmmeterService;
import cn.labbook.bluetooth.service.IApplyPayService;
import cn.labbook.bluetooth.service.IApplyRecordService;
import cn.labbook.bluetooth.service.IUserService;
import cn.labbook.bluetooth.util.DoubleUtils;
import cn.labbook.bluetooth.util.MessageUtil;
import cn.labbook.bluetooth.util.weixin.pay.PayUtil;
import cn.labbook.bluetooth.util.weixin.pay.UUIDHexGenerator;
import cn.labbook.bluetooth.util.weixin.pay.XmlUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: zhangyang
 * @Date: 2023/4/13 14:39
 * @Desc: 申请支付 service 实现
 */
@Service
public class ApplyPayServiceImpl extends ServiceImpl<ApplyPayMapper, ApplyPay> implements IApplyPayService {

    private final String mch_id = "1491927112";//商户号
    private final String spbill_create_ip = "127.0.0.1";//终端IP

    private final String domain = "https://bluetooth.zoercn.com";
    private final String notify_url = domain + "/bluetooth/v2/weixin/callbackTenant";

    private final String notify_url_landlord = domain + "/bluetooth/v2/weixin/callbackLandlord";
    private final String trade_type = "JSAPI";//交易类型
    private final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private final String key = "&key=zk123sqweqdsadZK9120392018490184"; // 商户支付密钥
    private final String appid = "wxafeeae8c758d838d";

    @Autowired
    private IAmmeterService ammeterService;
    @Autowired
    private IApplyRecordService applyRecordService;
    @Autowired
    private IUserService userService;


    @Override
    public PrepaymentResultDTO payment(String ammeterId, String openId, String totalFee, String body) {
        Ammeter ammeter = ammeterService.getById(ammeterId);
        // 房客付服务费
        if (ammeter.skimpYes()) {
            return paymentTenant(ammeter, openId, totalFee, body);
        }
        // 房东付服务费
        applyRecordService.create(ammeterId, openId, totalFee);

        // 发送短信
        MessageUtil.sendApplyMessage(userService.getMobilePhone(ammeter.getManagerId()), SmsTemplateEnum.TENANT_APPLY.getCode(), ammeter.getLocation());
        return null;
    }

    private PrepaymentResultDTO paymentTenant(
            Ammeter ammeter, String openId,
            String totalFee, String body
    ) {
        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String code = PayUtil.createCode(8);
        String out_trade_no = mch_id + today + code;//商户订单号

        ApplyPay applyPay = new ApplyPay();
        applyPay.setTenantPayId(out_trade_no);
        applyPay.setOpenId(openId);
        applyPay.setAmmeterId(ammeter.getAmmeterId());
        applyPay.setLocation(ammeter.getLocation());

        double totalFeeValue = Double.parseDouble(totalFee);
        double amount = totalFeeValue / 100;
        double protectPrice = ammeterService.getProtectPrice(ammeter.getProtectPrice());
        if (DoubleUtils.leZero(ammeter.getBalance()) && amount < protectPrice) {
            throw new SysException("申请金额不能小于保电金额");
        }

        if (DoubleUtils.leZero(ammeter.getBalance())) {
            applyPay.setProtectPrice(protectPrice);
        }
        double rate = applyRecordService.getPayRate(ammeter.getAdminId());
        BigDecimal rate_bv = new BigDecimal(rate).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP);
        BigDecimal zkisPlatformAmount = (new BigDecimal(totalFeeValue).multiply(rate_bv)).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
        applyPay.setZkisPlatformAmount(zkisPlatformAmount.doubleValue());
        applyPay.setAmount(amount);

        double payTrueAmount = amount;
        if (DoubleUtils.leZero(ammeter.getBalance())) {
            payTrueAmount =  amount - protectPrice;
            applyPay.setProtectPrice(protectPrice);
        }
        applyPay.setPayTrueAmount(payTrueAmount);
        applyPay.setCreateTime(new Date());
        applyPay.setStatus(ApplyPay.Status.WAIT_PAY.getKey());
        applyPay.setIsSkimp(ammeter.getIsSkimp());
        save(applyPay);
        return unifiedorder(zkisPlatformAmount.doubleValue() * 100, openId, body, out_trade_no, notify_url);
    }

    public PrepaymentResultDTO unifiedorder(
            double zkisPlatformAmount, String openId, String body, String out_trade_no, String notify_url
    ) {
        try {
            body = new String(body.getBytes("UTF-8"),"ISO-8859-1");
            String nonce_str = UUIDHexGenerator.generate();//随机字符串
            WXPayment paymentPo = new WXPayment();
            paymentPo.setTotal_fee(String.valueOf((int) zkisPlatformAmount));

            paymentPo.setAppid(appid);
            paymentPo.setMch_id(mch_id);
            paymentPo.setNonce_str(nonce_str);
            String newbody = new String(body.getBytes("ISO-8859-1"),"UTF-8");//以utf-8编码放入paymentPo，微信支付要求字符编码统一采用UTF-8字符编码
            paymentPo.setBody(newbody);
            paymentPo.setOut_trade_no(out_trade_no);
            paymentPo.setSpbill_create_ip(spbill_create_ip);
            paymentPo.setNotify_url(notify_url);
            paymentPo.setTrade_type(trade_type);
            paymentPo.setOpenid(openId);

            // 把请求参数打包成数组
            Map<String, Object> sParaTemp = new HashMap();
            sParaTemp.put("appid", paymentPo.getAppid());
            sParaTemp.put("mch_id", paymentPo.getMch_id());
            sParaTemp.put("nonce_str", paymentPo.getNonce_str());
            sParaTemp.put("body",  paymentPo.getBody());
            sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
            sParaTemp.put("total_fee",paymentPo.getTotal_fee());
            sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
            sParaTemp.put("notify_url",paymentPo.getNotify_url());
            sParaTemp.put("trade_type", paymentPo.getTrade_type());
            sParaTemp.put("openid", paymentPo.getOpenid());
            // 除去数组中的空值和签名参数
            Map sPara = PayUtil.paraFilter(sParaTemp);
            String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
            //MD5运算生成签名
            String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
            paymentPo.setSign(mysign);
            //打包要发送的xml
            String respXml = XmlUtil.messageToXML(paymentPo);
            // 打印respXml发现，得到的xml中有“__”不对，应该替换成“_”
            respXml = respXml.replace("__", "_");
            String param = respXml;
            //String result = SendRequestForUrl.sendRequest(url, param);//发起请求
            String result = PayUtil.httpRequest(url, "POST", param);
            // 将解析结果存储在Map中
            Map map = new HashMap();
            InputStream in=new ByteArrayInputStream(result.getBytes());
            // 读取输入流
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            // 得到xml根元素
            Element root = document.getRootElement();
            // 得到根元素的所有子节点
            List<Element> elementList = root.elements();
            for (Element element : elementList) {
                map.put(element.getName(), element.getText());
            }
            // 返回信息
            String return_code = map.get("return_code").toString();//返回状态码
            String result_code = map.get("result_code").toString();//返回状态码

            if(!("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code))){
                throw new SysException("预支付失败");
            }

            PrepaymentResultDTO dto = new PrepaymentResultDTO();
            String prepay_id = map.get("prepay_id").toString();//返回的预付单信息
            String nonceStr = UUIDHexGenerator.generate();
            dto.setNonceStr(nonceStr);
            dto.setPackageStr("prepay_id=" + prepay_id);

            Long timeStamp = System.currentTimeMillis() / 1000;
            dto.setTimeStamp(timeStamp + "");
            String stringSignTemp = "appId=" + appid + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
            //再次签名
            String paySign = PayUtil.sign(stringSignTemp, key, "utf-8").toUpperCase();
            dto.setPaySign(paySign);
            return dto;
        } catch (Exception e) {
            e.printStackTrace();
            throw new SysException(SysExceptionEnum.SYSTEM_ERROR);
        }
    }

    @Override
    public void callbackTenant(Map<String, Object> dataMap) {
        ApplyPay applyPay = getById(dataMap.get("out_trade_no").toString());
        if (Objects.isNull(applyPay)) {
            return;
        }

        String return_code = dataMap.get("return_code").toString();//返回状态码
        String result_code = dataMap.get("result_code").toString();//返回信息
        if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
            applyPay.setStatus(ApplyPay.Status.SUCCESS.getKey());
            applyRecordService.create(applyPay);
        } else {
            String err_code_des = dataMap.get("err_code_des").toString();
            applyPay.setFailReason(err_code_des);
        }
        updateById(applyPay);

        Ammeter ammeter = ammeterService.getById(applyPay.getAmmeterId());
        // 发送短信
        MessageUtil.sendApplyMessage(userService.getMobilePhone(ammeter.getManagerId()), SmsTemplateEnum.TENANT_APPLY.getCode(), ammeter.getLocation());
    }

    @Override
    public PrepaymentResultDTO paymentLandlord(Integer applyRecordId) {
        ApplyRecord applyRecord = applyRecordService.getById(applyRecordId);
        if (Objects.isNull(applyRecord)) {
            throw new SysException("查询申请记录失败");
        }

        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String code = PayUtil.createCode(8);
        String out_trade_no = mch_id + today + code;//商户订单号

        ApplyPay applyPay = new ApplyPay();
        applyPay.setTenantPayId(out_trade_no);
        applyPay.setOpenId(applyRecord.getOpenId());
        applyPay.setAmmeterId(applyRecord.getAmmeterId());
        applyPay.setLocation(applyRecord.getLocation());
        applyPay.setAmount(applyRecord.getAmount());
        applyPay.setZkisPlatformAmount(applyRecord.getZkisPlatformAmount());
        applyPay.setPayTrueAmount(applyRecord.getPayTrueAmount());
        applyPay.setCreateTime(new Date());
        applyPay.setStatus(ApplyPay.Status.WAIT_PAY.getKey());
        applyPay.setIsSkimp(applyRecord.getIsSkimp());
        applyPay.setApplyRecordId(applyRecordId);
        save(applyPay);

        String body = "房东支付服务费";
        return unifiedorder(applyRecord.getZkisPlatformAmount() * 100, applyRecord.getOpenId(), body, out_trade_no, notify_url_landlord);
    }

    @Override
    public void callbackLandlord(Map<String, Object> dataMap) {
        ApplyPay applyPay = getById(dataMap.get("out_trade_no").toString());
        if (Objects.isNull(applyPay)) {
            return;
        }

        String return_code = dataMap.get("return_code").toString();//返回状态码
        String result_code = dataMap.get("result_code").toString();//返回信息
        if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
            applyPay.setStatus(ApplyPay.Status.SUCCESS.getKey());
            applyRecordService.handle(applyPay.getApplyRecordId());
        } else {
            String err_code_des = dataMap.get("err_code_des").toString();
            applyPay.setFailReason(err_code_des);
        }
        updateById(applyPay);
    }

}
